package com.nd.fastdp.framework.controller;

import com.alibaba.fastjson.JSON;
import com.nd.fastdp.framework.exception.BusinessException;
import com.nd.fastdp.framework.exception.DaoException;
import com.nd.fastdp.framework.exception.FdpException;
import com.nd.fastdp.framework.pojo.constant.BaseEnum;
import com.nd.fastdp.framework.pojo.constant.RespCodeEnum;
import com.nd.fastdp.framework.pojo.vo.Result;
import com.nd.fastdp.utils.HttpServletRequestUtil;
import com.nd.fastdp.utils.HttpServletResponseUtil;
import com.nd.fastdp.utils.RequestDetail;
import com.nd.fastdp.utils.RequestDetailThreadLocal;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @description 对异常进行返回处理
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(FdpException.class)
    @ResponseStatus(HttpStatus.OK)
    public Object handle(FdpException e, HttpServletRequest request, HttpServletResponse response) {

        printRequestDetail();

        log.error("[FdpException] message={}", e);

        if(HttpServletRequestUtil.isAjax()){
            return Result.FAILD(e.getErrorCode(), e.getMessage());
        }

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("errorMsg", e.getMessage());

        modelAndView.setViewName("error/500");

        return modelAndView;
    }

    @ExceptionHandler(BusinessException.class)
    @ResponseStatus(HttpStatus.OK)
    public Object handle(BusinessException e, HttpServletRequest request, HttpServletResponse response) {

        printRequestDetail();

        log.error("[FdpException] message={}", e);

        if(HttpServletRequestUtil.isAjax()){
            return Result.FAILD(e.getErrorCode(), e.getMessage());
        }

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("errorMsg", e.getMessage());

        modelAndView.setViewName("error/500");

        return modelAndView;
    }

    @ExceptionHandler(DaoException.class)
    @ResponseStatus(HttpStatus.OK)
    public Object handle(DaoException e, HttpServletRequest request, HttpServletResponse response) {

        printRequestDetail();

        log.error("[FdpException] message={}", e);

        if(HttpServletRequestUtil.isAjax()){
            return Result.FAILD(e.getErrorCode(), e.getMessage());
        }

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("errorMsg", e.getMessage());

        modelAndView.setViewName("error/500");

        return modelAndView;
    }

    /**
     * 非法参数验证异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(value = HttpStatus.OK)
    public Object handleMethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException ex, HttpServletRequest request, HttpServletResponse response) {

        printRequestDetail();
        log.error("[MethodArgumentNotValidException] message={}", ex);

        BindingResult bindingResult = ex.getBindingResult();
        List<String> list = new ArrayList<>();
        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
        for (FieldError fieldError : fieldErrors) {
            list.add(fieldError.getDefaultMessage());
        }
        Collections.sort(list);

        log.error(getApiCodeString(RespCodeEnum.PARAM_ERROR) + ":" + JSON.toJSONString(list));

        if(HttpServletRequestUtil.isAjax()){
            return Result.FAILD(RespCodeEnum.PARAM_ERROR.getValue(), JSON.toJSONString(list));
        }

        ModelAndView modelAndView = new ModelAndView();

        modelAndView.addObject("errorMsg", JSON.toJSONString(list));
        modelAndView.setViewName("error/400");

        return modelAndView;
    }

    /**
     * 系统登录异常处理
     *
     * @param exception
     * @return
     */
    /*@ExceptionHandler(value = SysLoginException.class)
    @ResponseStatus(HttpStatus.OK)
    public ApiResult<Boolean> sysLoginExceptionHandler(SysLoginException exception) {
        printRequestDetail();
        printApiCodeException(ApiCode.LOGIN_EXCEPTION, exception);
        return ApiResult.fail(ApiCode.LOGIN_EXCEPTION);
    }*/


    /**
     * HTTP解析请求参数异常
     *
     * @param exception
     * @return
     */
    @ExceptionHandler(value = {HttpMessageNotReadableException.class})
    @ResponseStatus(HttpStatus.OK)
    public Object httpMessageNotReadableException(HttpMessageNotReadableException exception, HttpServletRequest request, HttpServletResponse response) {

        printRequestDetail();
        log.error("[HttpMessageNotReadableException] message={}", exception);

        if(HttpServletRequestUtil.isAjax()){
            return Result.FAILD(RespCodeEnum.PARAM_ERROR.getValue(), exception.getMessage());
        }

        ModelAndView modelAndView = new ModelAndView();

        modelAndView.addObject("errorMsg", exception.getMessage());
        modelAndView.setViewName("error/400");

        return modelAndView;
    }

    @ExceptionHandler(value = {HttpRequestMethodNotSupportedException.class})
    @ResponseStatus(HttpStatus.OK)
    public Object httpRequestMethodNotSupportedExceptionHandler(HttpRequestMethodNotSupportedException exception, HttpServletRequest request, HttpServletResponse response) {

        printRequestDetail();
        log.error("[HttpRequestMethodNotSupportedException] message={}", exception);

        if(HttpServletRequestUtil.isAjax()){
            HttpServletResponseUtil.printJson(response, Result.FAILD(RespCodeEnum.HTTP_BAD_METHOD.getValue(), exception.getMessage()));
            return null;
        }

        ModelAndView modelAndView = new ModelAndView();

        modelAndView.addObject("errorMsg", exception.getMessage());
        modelAndView.setViewName("error/405");

        return modelAndView;
    }

    /**
     * HTTP
     *
     * @param exception
     * @return
     */
    /*@ExceptionHandler(value = HttpMediaTypeException.class)
    @ResponseStatus(HttpStatus.OK)
    public ApiResult<Boolean> httpMediaTypeException(HttpMediaTypeException exception) {
        printRequestDetail();
        printApiCodeException(ApiCode.HTTP_MEDIA_TYPE_EXCEPTION, exception);
        return ApiResult.fail(ApiCode.HTTP_MEDIA_TYPE_EXCEPTION);
    }*/

    /**
     * 自定义业务/数据异常处理
     *
     * @param exception
     * @return
     */
    /*@ExceptionHandler(value = {SpringBootPlusException.class})
    @ResponseStatus(HttpStatus.OK)
    public ApiResult<Boolean> springBootPlusExceptionHandler(SpringBootPlusException exception) {
        printRequestDetail();
        log.error("springBootPlusException:", exception);
        int errorCode;
        if (exception instanceof BusinessException) {
            errorCode = ApiCode.BUSINESS_EXCEPTION.getCode();
        } else if (exception instanceof DaoException) {
            errorCode = ApiCode.DAO_EXCEPTION.getCode();
        } else if (exception instanceof VerificationCodeException) {
            errorCode = ApiCode.VERIFICATION_CODE_EXCEPTION.getCode();
        } else {
            errorCode = ApiCode.SPRING_BOOT_PLUS_EXCEPTION.getCode();
        }
        return new ApiResult<Boolean>()
                .setCode(errorCode)
                .setMessage(exception.getMessage());
    }*/


    /**
     * 默认的异常处理
     *
     * @param e
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseStatus(HttpStatus.OK)
    public Object exceptionHandler(Exception e, HttpServletRequest request, HttpServletResponse response) {

        printRequestDetail();
        log.error("[Exception] message={}", e);

        if(HttpServletRequestUtil.isAjax()){
            return Result.FAILD(RespCodeEnum.INTERNAL_SERVER_ERROR.getValue(), e.getMessage());
        }

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("errorMsg", e.getMessage());

        modelAndView.setViewName("error/500");

        return modelAndView;
    }


    /**
     * 打印请求详情
     */
    private void printRequestDetail() {

        RequestDetail requestDetail = RequestDetailThreadLocal.getRequestDetail();
        if (requestDetail != null) {
            log.error("异常来源：ip: {}, path: {}", requestDetail.getIp(), requestDetail.getPath());
        }
    }

    /**
     * 获取ApiCode格式化字符串
     *
     * @param apiCode
     * @return
     */
    private String getApiCodeString(BaseEnum apiCode) {
        if (apiCode != null) {
            return String.format("errorCode: %s, errorMessage: %s", apiCode.getValue(), apiCode.getDesc());
        }
        return null;
    }

    /**
     * 打印错误码及异常
     *
     * @param apiCode
     * @param exception
     */
    private void printApiCodeException(BaseEnum apiCode, Exception exception) {
        log.error(getApiCodeString(apiCode), exception);
    }

}

